package entities.concept;

import java.util.HashSet;
import java.util.Set;

import option.KBVocabulary;

import entities.KBEntity;
import entities.concept.PConcept;
import entities.role.Role;

/**
 * A general description of concepts
 * 
 * @author j55wu
 * 
 */
public abstract class Concept implements KBEntity{

	public static final String PRIME = "*";

	// Concept subclasses should implement functionality to return a "primed"
	// copy of themselves
	public abstract Concept getPrimeCopy();
	
	public abstract boolean hasUniversalInside();

	public abstract String toSerializedString();

	public abstract Set<Role> extractFeatures();

	public abstract Set<Role> extractRoles();
	
	//sinature: A, R, R-, {a}
	public abstract Set<KBEntity> extractSig();

	public abstract Set<String> extractConstants();

	public abstract Set<AtomicConcept> extracAtomicConcepts();

	public abstract boolean isParameterized();

	public abstract Concept makeParameterized();

	public abstract Concept initParameters(Role f, String v);

	// note that no simplification is done, e.g., not (not A) = A. This makes a
	// difference for absorption, so mind your coding!
	public static HashSet<Concept> reduceToDisjuncts(Concept c) {
		HashSet<Concept> ors = new HashSet<Concept>();
		if (c instanceof OrConcept) {
			OrConcept oc = (OrConcept) c;
			for (Concept cc : oc.getConceptSet()) {
				ors.addAll(reduceToDisjuncts(cc));
			}
		} else {
			ors.add(c);
		}
		return ors;
	}
	
	//specifically for Pd concept, which is always horn-style
	public static Concept simplifyConjunctiveConcept(Concept ac){
		Set<Concept> ands = reduceToConjuncts(ac);
		Set<Concept> rm = new HashSet<Concept>();
		for(Concept c:ands){
			if(c.isTop()){
				rm.add(c);
			}
		}
		ands.removeAll(rm);
		return getConjunctiveConcept(ands);
	}

	public static Set<Concept> reduceToConjuncts(Concept c) {
		HashSet<Concept> ands = new HashSet<Concept>();
		if (c instanceof AndConcept) {
			AndConcept ac = (AndConcept) c;
			for (Concept cc : ac.getConceptSet()) {
				ands.addAll(reduceToConjuncts(cc));
			}
		} else {
			ands.add(c);
		}
		return ands;
	}

	public boolean isTop() {
		if(this.equals(KBVocabulary.CONCEPT_TOP)){
			return true;
		}
//		if (this instanceof PConcept) {
//			PConcept pc = (PConcept) this;
//			return pc.isTop();
//		} else if (this instanceof OrConcept) {
//			boolean ret = false;
//			OrConcept or = (OrConcept) this;
//			for (Concept o : or.getConceptSet()) {
//				ret |= o.isTop();
//			}
//			if (ret) {
//				return ret;
//			}
//		} else 
		if (this instanceof NotConcept) {
			NotConcept nc = (NotConcept) this;
			return nc.getConcept().isBot();
		}
		return false;
	}

	public boolean isBot() {
		if(this.equals(KBVocabulary.CONCEPT_BOTTOM)){
			return true;
		}
//		if (this instanceof PConcept) {
//			PConcept pc = (PConcept) this;
//			return pc.isBottom();
//		}
		/*
		 * else if(this.isBinarySimple()){ // A and B, to see if B is not A
		 * AndConcept as = (AndConcept) this; Iterator<Concept> i =
		 * as.getConceptSet().iterator(); Concept a = i.next(); Concept b =
		 * i.next(); if(a instanceof NotConcept){ NotConcept n = (NotConcept)a;
		 * 
		 * } }
		 */

		if (this instanceof NotConcept) {
			return ((NotConcept) this).getConcept().isTop();
		}

		return false;
	}
	
	public boolean isRoleGuard() {
		if (this instanceof PConcept) {
			PConcept pc = (PConcept) this;
			return pc.isRoleGuard();
		}
		return false;
	}

	public boolean isGuard() {
		if (this instanceof PConcept) {
			PConcept pc = (PConcept) this;
			return pc.isGuard();
		}
		return false;
	}

	public boolean isAtomic() {
		return this instanceof AtomicConcept;
	}

	public boolean isBinarySimple() {
		if (this instanceof AndConcept) {
			AndConcept ands = (AndConcept) this;
			if (ands.getConceptSet().size() != 2) {
				return false;
			}
			for (Concept con : ands.getConceptSet()) {
				if (!(con.isAtomic())) {
					return false;
				}
			}
			return true;
		}
		return false;
	}

	// i.e., f op k OR k op f (exactly one feature and one value)
	public boolean isStandardDomainConcept() {
		if (this instanceof DomainConcept) {
			DomainConcept<?, ?> dc = (DomainConcept<?, ?>) this;
			return (dc.isLHSRole() && dc.isRHSString())
					|| (dc.isRHSRole() && dc.isLHSString());
		}
		return false;
	}

	// negated concept names
	public boolean isSimpleNot() {
		if (this instanceof NotConcept) {
			NotConcept nc = (NotConcept) this;
			if (nc.getConcept().isPrimitive()) {
				return true;
			}
		}
		return false;
	}
	//a PConcept that is neither TOP or bottom
	public boolean isPrimitive(){
		if(!this.isTop() && !this.isBot() && (this instanceof PConcept)){
			return true;
		}
		return false;
	}
	
	//if A or ~A
	public boolean isLiteral(){
		return this.isPrimitive() || this.isSimpleNot();
	}

	public static Concept getConjunctiveConcept(Set<Concept> cs) {
		if (cs.isEmpty()) {
			return KBVocabulary.CONCEPT_TOP;
		}
		if (cs.size() == 1) {
			return cs.iterator().next();
		} else {
			return new AndConcept(cs);
		}
	}
	
	public static Concept getDisjunctiveConcept(Set<Concept> cs) {
		if (cs.isEmpty()) {
			return KBVocabulary.CONCEPT_BOTTOM;
		}
		if (cs.size() == 1) {
			return cs.iterator().next();
		} else {
			return new OrConcept(cs);
		}
	}

}
